`include "mycpu.h"
/*
取指阶段异常发生处理过程：

1、req已经拉起，addr_ok未到（还未addr握手）：
    fs_inst_cancel拉起直到addr_ok到达，之后pfs_eentry_req_r拉起（表示异常入口指令正在请求）
2、req与addr_ok同时拉起（当前拍正在addr握手）：
    下一拍pfs_eentry_req_r拉起，继续req（请求异常入口指令）
3、pfs_addr_ok_r为1，还未data_ok（已经addr握手，还未data握手）：
    下一拍pfs_eentry_req_r拉起，开始req（请求异常入口指令）
4、data_ok为1（当前拍正在data握手）
    下一拍pfs_eentry_req_r拉起，开始req（请求异常入口指令）
5、指令缓存在IF中
    下一拍pfs_eentry_req_r拉起，开始req（请求异常入口指令）

--> 时间
                    addr握手              data握手
                       |                     |
            |          |          |          |             |
------------|----------|--        |          |             |
            |        --|--        |          |             |
            |          |  --------|----------|--           |
            |          |          |        --|--           |
            |          |          |          |  -----------|----------
            1          2          3          4             5
*/
module if_stage(
    input clk,
    input reset,
    //ID allowin
    input ds_allowin,
    //32: branch valid 31-0: branch target
    input [`BR_BUS_WD - 1:0] br_bus,
    //IF stage to ID stage
    output fs_to_ds_valid,
    output [`FS_TO_DS_WD - 1:0] fs_to_ds_bus,
    //exception
    input exec_flush,
    input [31:0] fs_ex_entry,
    //inst_sram
    output inst_sram_req,
    output inst_sram_wr,
    output [1:0] inst_sram_size,
    output [3:0] inst_sram_wstrb,
    output [31:0] inst_sram_addr,
    output [31:0] inst_sram_wdata,
    input inst_sram_addr_ok,
    input inst_sram_data_ok,
    input [31:0] inst_sram_rdata,
    //tlb s0 to WS
    output [18:0] s0_vppn,
    output s0_va_bit12,
    input s0_found,
    input [3:0] s0_index,
    input [19:0] s0_ppn,
    input [5:0] s0_ps,
    input [1:0] s0_plv,
    input [1:0] s0_mat,
    input s0_d,
    input s0_v,
    //crmd_da/crmd_pg crmd_plv
    input [1:0] csr_crmd_plv,
    input csr_crmd_da,
    input csr_crmd_pg,
    //dmw0
    input csr_dmw0_plv0,
    input csr_dmw0_plv3,
    input [1:0] csr_dmw0_mat,
    input [2:0] csr_dmw0_pseg,
    input [2:0] csr_dmw0_vseg,
    //dmw1
    input csr_dmw1_plv0,
    input csr_dmw1_plv3,
    input [1:0] csr_dmw1_mat,
    input [2:0] csr_dmw1_pseg,
    input [2:0] csr_dmw1_vseg,
    //datf
    input [1:0] csr_crmd_datf,
    //mtype
    output inst_mtype,
    //cacop wait
    output fs_wait_data_ok,
    input es_cacop_rdy
);
    wire br_stall;
    wire pfs_to_fs_valid;
    wire pfs_ready_go;
    wire pfs_allowin;
    reg pfs_req_en;

    wire fs_inst_cancel;
    reg exc_flush_r_valid;
    reg [31:0] exc_entry_r;

    reg pfs_addr_ok_r;
    reg pfs_eentry_req_r;
    wire pfs_addr_ok;
    reg fs_data_ok_r;
    wire fs_data_ok;

    reg [31:0] fs_inst_buf;
    reg fs_inst_buf_valid;

    reg br_taken_r;
    reg [31:0] br_target_r;


    //fs valid bit
    wire fs_allowin;
    reg fs_throw;
    reg fs_valid;
    wire fs_ready_go;
    //pc
    reg [31:0] fs_pc;
    wire [31:0] nextpc;
    wire [31:0] seq_pc;
    wire [31:0] fs_inst;
    //exp13: ADEF
    wire fs_ex_adef;
    wire pfs_ex_adef;
    reg fs_ex_adef_r;
    //exp19:
    wire pfs_ex_pif, fs_ex_pif;
    wire pfs_ex_ppi, fs_ex_ppi;
    wire pfs_ex_tlbr, fs_ex_tlbr;
    wire pfs_dmw0_hit, fs_dmw0_hit;
    wire pfs_dmw1_hit, fs_dmw1_hit; 
    wire pfs_tlb_hit, fs_tlb_hit;
    reg fs_ex_tlbr_r;
    reg fs_ex_pif_r;
    reg fs_ex_ppi_r;
    reg [31:0] fs_ex_baddr_r;
    wire [31:0] dmw0_addr;
    wire [31:0] dmw1_addr;
    wire [31:0] pfs_tlb_paddr;
    wire [31:0] fs_ex_baddr;

    assign fs_ex_adef = fs_ex_adef_r;
    assign fs_ex_tlbr = fs_ex_tlbr_r;
    assign fs_ex_pif = fs_ex_pif_r;
    assign fs_ex_ppi = fs_ex_ppi_r;
    assign fs_ex_baddr = fs_ex_baddr_r;
    assign pfs_ex_adef = (nextpc[0] || nextpc[1]) || 
                (nextpc[31] && (csr_crmd_plv == 2'b11)
                 && !((pfs_dmw0_hit && csr_dmw0_pseg[2]) || (pfs_dmw1_hit && csr_dmw1_pseg[2])));
    always @(posedge clk) begin
        if(reset) begin
            fs_ex_adef_r <= 1'b0;
            fs_ex_tlbr_r <= 1'b0;
            fs_ex_pif_r <= 1'b0;
            fs_ex_ppi_r <= 1'b0;
            fs_ex_baddr_r <= 32'b0;
        end
        else if(inst_sram_req && pfs_addr_ok && fs_allowin) begin
            fs_ex_adef_r <= pfs_ex_adef;
            fs_ex_tlbr_r <= pfs_ex_tlbr;
            fs_ex_pif_r <= pfs_ex_pif;
            fs_ex_ppi_r <= pfs_ex_ppi;
            fs_ex_baddr_r <= nextpc;
        end
    end

    assign fs_to_ds_bus[`FS_TO_DS_WD - 1:0] = {
        fs_ex_baddr[31:0],   //99:68
        fs_ex_ppi,           //67:67 
        fs_ex_pif,           //66:66
        fs_ex_tlbr,          //65:65
        fs_ex_adef,          //64:64
        fs_inst[31:0],       //63:32
        fs_pc[31:0]          //31:0
    };
    assign inst_sram_wr = 1'b0;
    assign inst_sram_wstrb[3:0] = {4{1'b0}};
    assign inst_sram_wdata[31:0] = 32'b0;
//pre-IF
    assign inst_sram_size[1:0] = {1'b1, 1'b0};
    assign inst_sram_req = ~reset & fs_allowin & pfs_req_en & ~br_stall & ~es_cacop_rdy;
    assign inst_sram_addr = {32{csr_crmd_da}} & nextpc
                          | {32{pfs_dmw0_hit}} & dmw0_addr
                          | {32{pfs_dmw1_hit}} & dmw1_addr
                          | {32{pfs_tlb_hit}} & pfs_tlb_paddr;


    assign br_stall = br_bus[33];

    assign pfs_allowin = pfs_ready_go & fs_allowin;
    assign pfs_ready_go = pfs_addr_ok & inst_sram_req;
    assign pfs_to_fs_valid  = ~reset & pfs_ready_go;
    assign pfs_addr_ok = inst_sram_addr_ok;

    assign fs_inst_cancel = exec_flush | exc_flush_r_valid;  
    assign nextpc[31:0] = {32{pfs_eentry_req_r}} & exc_entry_r[31:0]
                        | {32{~br_taken_r & br_bus[32] & ~pfs_eentry_req_r}} & br_bus[31:0]
                        | {32{br_taken_r & ~pfs_eentry_req_r}} & br_target_r[31:0]
                        | {32{~pfs_eentry_req_r & ~(~br_taken_r & br_bus[32])
                               & ~br_taken_r}} & seq_pc;
    assign seq_pc = fs_pc + 32'h4;

    //pfs_req_en : inst_sram_req enable bit
        always @(posedge clk) begin
            if(reset)
                pfs_req_en <= 1'b1;
            else if(inst_sram_req & pfs_addr_ok & ~fs_inst_cancel)
                pfs_req_en <= 1'b0;
            else if(fs_to_ds_valid & ds_allowin)
                pfs_req_en <= 1'b1;
            else if(fs_inst_cancel)
                pfs_req_en <= 1'b1;
        end
    //pfs_eentry_req_r
        always @(posedge clk) begin
            if(reset)
                pfs_eentry_req_r <= 1'b0;
            else if(inst_sram_req && pfs_addr_ok && pfs_eentry_req_r)
                pfs_eentry_req_r <= 1'b0;
            else if(inst_sram_req && pfs_addr_ok && fs_inst_cancel)
                pfs_eentry_req_r <= 1'b1;
            else if(pfs_addr_ok_r && fs_inst_cancel)
                pfs_eentry_req_r <= 1'b1;
        end
    //pfs_addr_ok_r: already shaked hands, wait for data_ok
        always @(posedge clk) begin
            if(reset)
                pfs_addr_ok_r <= 1'b0;
            else if(inst_sram_req & pfs_addr_ok)
                pfs_addr_ok_r <= 1'b1;
            else if(fs_data_ok)
                pfs_addr_ok_r <= 1'b0;
        end
    //br r: buffer for branch taken and branch target
        always @(posedge clk) begin
            if(reset)
                br_taken_r <= 1'b0;
            else if(pfs_ready_go)
                br_taken_r <= 1'b0;
            else if(inst_sram_req & ~pfs_addr_ok & br_bus[32])
                br_taken_r <= 1'b1;
        end
        always @(posedge clk) begin
            if(~br_stall && br_bus[32])
                br_target_r <= br_bus[31:0];
        end
    //ex r: buffer for exc_flush and exc_entry
        always @(posedge clk) begin
            if(reset)
                exc_flush_r_valid <= 1'b0;
            else if(pfs_to_fs_valid && fs_allowin)
                exc_flush_r_valid <= 1'b0;
            else if(exec_flush && ~pfs_addr_ok)
                exc_flush_r_valid <= 1'b1;
        end
        always @(posedge clk) begin
            if(exec_flush)
                exc_entry_r <= fs_ex_entry;
        end
//IF stage
    assign fs_ready_go = (fs_data_ok | fs_data_ok_r) & ~fs_throw & ~fs_inst_cancel;
    assign fs_allowin = !fs_valid || fs_ready_go && ds_allowin;
    assign fs_to_ds_valid = fs_valid && fs_ready_go;
    assign fs_data_ok = inst_sram_data_ok;
    assign fs_inst = fs_inst_buf_valid ? fs_inst_buf : inst_sram_rdata;
    //fs_valid & fs_pc
        always @(posedge clk) begin
            if(reset) begin
                fs_pc <= 32'h1bfffffc;
            end
            else if(fs_allowin && pfs_to_fs_valid)begin
                fs_pc <= nextpc;
            end
        end
        always @(posedge clk) begin
            if(reset) begin
                fs_valid <= 1'b0;
            end
            else if((fs_inst_cancel || pfs_eentry_req_r) && !fs_allowin) begin
                fs_valid <= 1'b0;
            end
            else if(fs_allowin)begin
                fs_valid <= pfs_to_fs_valid;
            end
        end
    //fs_data_ok_r: buffer for inst_sram_data_ok
        always @(posedge clk) begin
            if(reset)
                fs_data_ok_r <= 1'b0;
            else if(ds_allowin)
                fs_data_ok_r <= 1'b0;
            else if(fs_data_ok && !ds_allowin)
                fs_data_ok_r <= 1'b1;
        end
    //fs_inst_buf: buffer for inst from sram when ds_allowin is 1'b0
        always @(posedge clk) begin
            if(reset)
                fs_inst_buf_valid <= 1'b0;
            else if(ds_allowin || fs_inst_cancel)
                fs_inst_buf_valid <= 1'b0;
            else if(fs_data_ok && !ds_allowin && !fs_inst_cancel && !fs_throw)
                fs_inst_buf_valid <= 1'b1;
        end
        always @(posedge clk) begin
            if(fs_data_ok)
                fs_inst_buf <= inst_sram_rdata;
        end
    //fs_throw: 1'b1 when next read data in IF is invalid.
        always @(posedge clk) begin
            if(reset)
                fs_throw <= 1'b0;
            else if(fs_data_ok)
                fs_throw <= 1'b0;
            else if(exec_flush && (inst_sram_req || pfs_addr_ok_r) && ~fs_ready_go)
                fs_throw <= 1'b1;
        end


//address trans
    assign pfs_dmw0_hit = (nextpc[31:29] == csr_dmw0_vseg) && 
                    ((csr_crmd_plv == 2'b00 && csr_dmw0_plv0) | (csr_crmd_plv == 2'b11 && csr_dmw0_plv3))
                    && ~csr_crmd_da;

    assign pfs_dmw1_hit = (nextpc[31:29] == csr_dmw1_vseg) && 
                    ((csr_crmd_plv == 2'b00 && csr_dmw1_plv0) | (csr_crmd_plv == 2'b11 && csr_dmw1_plv3))
                    && ~csr_crmd_da;

    assign dmw0_addr = {csr_dmw0_pseg[2:0], nextpc[28:0]};

    assign dmw1_addr = {csr_dmw1_pseg[2:0], nextpc[28:0]};

    assign pfs_tlb_paddr = s0_ps == 6'd22 ? {s0_ppn[19:10], nextpc[21:0]} :
                                        {s0_ppn[19:0], nextpc[11:0]};
    
    assign s0_vppn = nextpc[31:13];

    assign s0_va_bit12 = nextpc[12];

    assign pfs_tlb_hit = s0_found & ~pfs_dmw0_hit & ~pfs_dmw1_hit & ~csr_crmd_da;
//exp19: exception
    assign pfs_ex_tlbr = ~pfs_dmw0_hit & ~pfs_dmw1_hit & ~csr_crmd_da & ~s0_found;

    assign pfs_ex_pif = pfs_tlb_hit & ~s0_v;

    assign pfs_ex_ppi = pfs_tlb_hit && s0_v && (csr_crmd_plv > s0_plv);
//exp21-22
    assign inst_mtype = csr_crmd_da & csr_crmd_datf[0]
                      | pfs_dmw0_hit & csr_dmw0_mat[0]
                      | pfs_dmw1_hit & csr_dmw1_mat[0]
                      | pfs_tlb_hit & s0_mat[0];
//exp23
    assign fs_wait_data_ok = (pfs_addr_ok_r | (inst_sram_req & ~inst_sram_addr_ok))
                            & ~inst_sram_data_ok;
endmodule